/******************************************************************************
	This file is a part of the Snapshot Bioinformatics Project

	Copyright (C) 2012-2013 Jarom Schow

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/

#include "TaxonUpdater.h"

#include <QtCore/QFile>
#include <QtCore/QTime>

#include "Core.h"
#include "DmpParser.h"
#include "FileDownload.h"
#include "FileUtil.h"
#include "Progress.h"
#include "TaxonUpdateTree.h"

using namespace Bio;

const int		c_bufferSize = 65536;
const QString	c_taxonUrl = "ftp://ftp.ncbi.nlm.nih.gov/pub/taxonomy/taxdump.tar.gz";
const QString	c_targetFile = "taxdump.tar.gz";
const QString	c_extractDir = "taxdump/";
const QString	c_nodesFile = "nodes.dmp";
const QString	c_namesFile = "names.dmp";

/**
**/
TaxonUpdater::TaxonUpdater()
{
}

/**
**/
TaxonUpdater::~TaxonUpdater()
{
}

/**
**/
bool TaxonUpdater::update( SqlServer &server, const QString &tempDir )
{
	Progress	progress( "Updating taxonomy...", 6 );
	
	m_path = tempDir;

	if( m_path.isEmpty() ){
		m_path = "./" + c_extractDir;
		m_filename = c_targetFile;
	}
	else {
		m_path.replace( '\\', '/' );
		if( !m_path.endsWith( '/' ) )
			m_path.append( '/' );
		m_filename = m_path + c_targetFile;
		m_path += c_extractDir;
	}

	if( !downloadFile() )
		return false;

	progress.step();

	if( !extractFile() )
		return false;

	progress.step();

	TaxonUpdateTree	tree;

	if( !loadExisting( server, tree ) )
		return false;

	progress.step();

	if( !readNodes( tree ) )
		return false;
	
	progress.step();

	if( !readNames( tree ) )
		return false;

	progress.step();
	tree.finalize( server );

	if( !updateDB( server, tree ) )
		return false;

	progress.step();
	progress.finish();

	return true;
}

/**
**/
bool TaxonUpdater::downloadFile()
{
	if( QFile::exists( m_filename ) )
		return true;

	Core::log( "Downloading taxonomy file..." );

	FileDownload	download( m_filename );
	QTime			timer;

	timer.start();

	if( !download.downloadSynchronous( c_taxonUrl ) )
		return false;

	int	elapsed = timer.elapsed() / 1000;

	Core::log( QString( "File downloaded in %1 min %2 secs" ).arg( elapsed / 60 ).arg( elapsed % 60 ) );

	return true;
}

/**
**/
bool TaxonUpdater::extractFile()
{
	if( QFile::exists( m_path + c_nodesFile ) && QFile::exists( m_path + c_namesFile ) )
		return true;

	Core::log( "Extracting taxonomy file..." );

	QTime	timer;

	timer.start();

	if( !FileUtil::extractTarFile( m_filename,  m_path, true ) ){
		Core::log( "Error extracting file." );
		return false;
	}

	int	elapsed = timer.elapsed() / 1000;

	Core::log( QString( "File extracted in %1 min %2 secs" ).arg( elapsed / 60 ).arg( elapsed % 60 ) );

	return true;
}

/**
**/
bool TaxonUpdater::loadExisting( SqlServer &server, TaxonUpdateTree &tree )
{
	QTime	timer;

	Core::log( "Reading existing taxa from database..." );

	timer.start();

	if( !tree.loadFromDB( server ) )
		return false;

	int	elapsed = timer.elapsed() / 1000;

	Core::log( QString( "Existing taxa loaded in %1 min %2 secs" ).arg( elapsed / 60 ).arg( elapsed % 60 ) );

	return true;
}

/**
**/
bool TaxonUpdater::readNodes( TaxonUpdateTree &tree )
{
	QTime	timer;

	Core::log( "Reading nodes file..." );

	NodesDmpParser		nodesParser( tree );

	timer.start();

	if( !nodesParser.parseFile( m_path + c_nodesFile ) )
		return false;

	int	elapsed = timer.elapsed() / 1000;

	Core::log( QString( "Nodes parsed in %1 min %2 secs" ).arg( elapsed / 60 ).arg( elapsed % 60 ) );

	return true;
}

/**
**/
bool TaxonUpdater::readNames( TaxonUpdateTree &tree )
{
	QTime	timer;

	Core::log( "Reading names file..." );

	NamesDmpParser	namesParser( tree );

	timer.start();

	if( !namesParser.parseFile( m_path + c_namesFile ) )
		return false;

	int	elapsed = timer.elapsed() / 1000;

	Core::log( QString( "Names parsed in %1 min %2 secs" ).arg( elapsed / 60 ).arg( elapsed % 60 ) );

	return true;
}

/**
**/
bool TaxonUpdater::updateDB( SqlServer &server, TaxonUpdateTree &tree )
{
	QTime	timer;

	Core::log( "Inserting taxa in database..." );

	timer.start();

	if( !tree.updateDB( server ) )
		return false;

	int	elapsed = timer.elapsed() / 1000;

	Core::log( QString( "Taxa inserted in %1 min %2 secs" ).arg( elapsed / 60 ).arg( elapsed % 60 ) );

/*	QFile	outfile( "Nodes.txt" );

	if( !outfile.open( QIODevice::WriteOnly ) )
		return false;

	QTextStream	os( &outfile );

	tree.print( os );

	outfile.close();

	elapsed = timer.elapsed() / 1000;
	output( QString( "Tree written to file in %1 min %2 secs" ).arg( elapsed / 60 ).arg( elapsed % 60 ) );
*/
	return true;
}